package fatboy.hypeerweb;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.util.Collection;
import java.util.TreeSet;

import junit.framework.Assert;

import org.junit.Test;


public class NodeAddressTest {
	
	private static final int[] OLD_CHILD = {1,2,4,8,13};
	private static final int[] PARENT = {0,0,0,0,5};
	private static final int[] NEW_CHILD = {-1,1,2,4,-1};
	/**
	 * test the toAddress and asInt methods to ensure proper function
	 */
	@Test
	public void testConstrucorAndAsInt(){
		final int x = 11;
		
		NodeAddress test = new NodeAddress(x);
		assertEquals(x, NodeAddress.asInt(test));
	}
	/**
	 * test that the midPoint function returns the proper midPoint
	 */
	@Test
	public void testMidPoint(){
		final int x = 12;
		final int y = 241;
		int mid = (x+y)/2;
		NodeAddress min = NodeAddress.toAddress(x);
		NodeAddress max = NodeAddress.toAddress(y);
		NodeAddress midPoint = NodeAddress.midPoint(min, max);
		assertEquals(mid, NodeAddress.asInt(midPoint));
	}
	/** 
	 * test get child method returns the correct address
	 */
	@Test
	public void testGetChild(){
		final int parent = 34;
		final int upPointer = 146;
		final int expected = 162;
		NodeAddress parentAdd = new NodeAddress(parent);
		NodeAddress upPointerAdd =new NodeAddress(upPointer);
		NodeAddress expectedAdd = new NodeAddress(expected);
		assertEquals(expectedAdd, parentAdd.getChild(upPointerAdd));
	}
	/**
	 * tests that that it picks the correct path
	 */
	@Test
	public void testOnPathToCorrect(){
		final int spotInt = 22;
		NodeAddress spot = new NodeAddress(spotInt);
		final int[] neighborsInt = {30,23,18,44};
		Collection<Node> neighbors = new TreeSet<Node>();
		for(int i = 0; i < neighborsInt.length; i++){
			Node curNeighbor = new Node(Integer.toBinaryString(neighborsInt[i]),
								NodeType.INTERIOR_NODE, new NodeAddress(neighborsInt[i]));
			neighbors.add(curNeighbor);
		}
		NodeAddress dest = new NodeAddress(0);
		final int expectedRet = 18;
		NodeAddress expected= new NodeAddress(expectedRet);
		assertEquals(expected, spot.onPathTo(dest, neighbors).getAddress());
	}
	/**
	 * tests that that it picks a random path
	 */
	@Test
	public void testOnPathToRandom(){
		final int spotInt = 23;
		NodeAddress spot = new NodeAddress(spotInt);
		final int[] neighborsInt = {22,21,19,7};
		Collection<Node> neighbors = new TreeSet<Node>();
		for(int i = 0; i < neighborsInt.length; i++){
			Node curNeighbor = new Node(Integer.toBinaryString(neighborsInt[i]),
								NodeType.INTERIOR_NODE, new NodeAddress(neighborsInt[i]));
			neighbors.add(curNeighbor);
		}
		NodeAddress dest = new NodeAddress(0);
		final int expectedReturnSize = 4;
		Collection<Node> returns = new TreeSet<Node>();
		final int tries = 25;
		for(int i  = 0 ; i < tries; i++){
			returns.add(spot.onPathTo(dest, neighbors));
		}
		assertEquals(expectedReturnSize,returns.size());
	}
	/**
	 * tests that the equals method properly works
	 */
	@Test
	public void testEquals(){
		NodeAddress first = new NodeAddress(0);
		NodeAddress second = new NodeAddress(1);
		NodeAddress third = new NodeAddress(0);
		assertFalse(first.equals(second));
		assertTrue(first.equals(third));
	}
	/**
	 * tests that the proper int is returned from the hashCode method
	 */
	@Test
	public void testHashCode(){
		NodeAddress first = new NodeAddress(0);
		NodeAddress second = new NodeAddress(1);
		assertEquals(first.hashCode() , 0);
		assertEquals(second.hashCode() , 1);
	}
	/**
	 * tests that the proper String is returned from the toString method
	 */
	@Test
	public void testToString(){
		NodeAddress first = new NodeAddress(0);
		NodeAddress second = new NodeAddress(2);
		assertEquals(first.toString() , "0 (0)");
		assertEquals(second.toString() , "10 (2)");
	}
	/**
	 * tests that arguments are thrown for invalid input
	 */
	@Test (expected = IllegalArgumentException.class)
	public void testInvalidInputs()
	{
		NodeAddress first = null;
		NodeAddress second = null;
		try{
			 first = new NodeAddress(Integer.MAX_VALUE);
			 second = new NodeAddress(1);
		}catch(IllegalArgumentException e){
			fail();	//this is impossible to cover
		}			//without making JUnit fail
		new NodeAddress(-1);
	}
	/**
	 * test get next younget Child method for boundary cases
	 */
	@Test
	public void testNextYoungest(){
		for(int i = 1 ; i < OLD_CHILD.length-1; i++){
			NodeAddress child = new NodeAddress(OLD_CHILD[i]);
			NodeAddress parent = new NodeAddress(PARENT[i]);
			NodeAddress test = child.getNextYoungestChild(parent);
		//	System.out.println("Child: " + child + "\nParent: " + parent + "\n Result: " +test);
			Assert.assertEquals(new NodeAddress(NEW_CHILD[i]), test);
		}
		NodeAddress child = new NodeAddress(OLD_CHILD[0]);
		NodeAddress parent = new NodeAddress(PARENT[0]);
		NodeAddress test = child.getNextYoungestChild(parent);
		//System.out.println("Child: " + child + "\nParent: " + parent + "\n Result: " +test);
		Assert.assertNull(test);
		final int last = 4;
		child = new NodeAddress(OLD_CHILD[last]);
		parent = new NodeAddress(PARENT[last]);
		test = child.getNextYoungestChild(parent);
		//System.out.println("Child: " + child + "\nParent: " + parent + "\n Result: " +test);
		Assert.assertNull(test);
		try{
			child = new NodeAddress(0);
			final int notChild = 3;
			parent = new NodeAddress(notChild);
			test = child.getNextYoungestChild(parent);
			fail();
		}catch (IllegalArgumentException e){
			int i = 0;
			i++;
		}
	}
}
